/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.ssau.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.rmi.RemoteException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jdom.Element;
import ru.ssau.datamanager.DataManager;
import ru.ssau.lib.InvalidValueException;
import ru.ssau.entities.DataAccessException;
import ru.ssau.entities.Message;
import ru.ssau.entities.User;
import ru.ssau.lib.StringIntegerPair;
import ru.ssau.utils.StringUtils;
import ru.ssau.utils.XMLHelper;

@WebServlet(name="ForumManagerServlet", urlPatterns={"/request"})
public class ForumManagerServlet extends HttpServlet {

    @EJB
    private DataManager dataManager;

    @Override
    public void init() {

    }

    @Override
    public void destroy() {
        
    }

    /** 
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        PrintWriter out =  response.getWriter();
        HttpSession session = request.getSession();
        //Атрибут message - сообщение об ошибке, которое выводится на отдельной
        //jsp-странице, если в коде сервлета возникнет исключение
        session.setAttribute("message", null);

        try {
            //Зпрос по умолчанию - запрос всех данных
            String action = request.getParameter(Constants.ACTION);
            if(StringUtils.isEmpty(action)) {
                action = Constants.GET_MODEL_STATE;
            }
            //Запрос на получение всех данных для отображения
            if(action.equals(Constants.GET_MODEL_STATE)) {
                Element userList = new Element("userList");
                XMLHelper.addElement(userList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(dataManager.getAllUsers()));
                session.setAttribute(Constants.USER_LIST, XMLHelper.outputXML(userList));

                Element threadList = new Element("threadList");
                XMLHelper.addElement(threadList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(dataManager.getAllThreads()));
                session.setAttribute(Constants.THREAD_LIST, XMLHelper.outputXML(threadList));

                Element messageList = new Element("messageList");
                XMLHelper.addElement(messageList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(dataManager.getAllMessages()));
                session.setAttribute(Constants.MESSAGE_LIST, XMLHelper.outputXML(messageList));

                getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
            }

            /*User section*/
            /*Асинхронное получение данных о user-е*/
            else if(action.equals(Constants.GET_USER)) {
                String value = (String) request.getParameter(Constants.ID);
                if(!StringUtils.isEmpty(value)) {
                    Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                    String userXML = XMLHelper.outputXML(dataManager.getUserById(id).toXMLElement());
                    out.print(userXML);
                }
            } 
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.ADD_USER)) {
                session.setAttribute(Constants.USER, null);
                getServletContext().getRequestDispatcher("/editUser.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет добавление user-а в хранилище данных*/
            else if(action.equals(Constants.DO_ADD_USER)) {
                String name = request.getParameter(Constants.NAME);
                if(StringUtils.isEmpty(name)) throw new InvalidValueException("Name field is empty");
                Date regDate;
                String setCurrentDateOption = request.getParameter(Constants.DATE_SET_OPTION);
                if(setCurrentDateOption == null) {
                    regDate = parseDate(request);
                } else {
                    regDate = new Date();
                }
                String info = request.getParameter(Constants.INFO);
                String login = request.getParameter(Constants.LOGIN);
                if(StringUtils.isEmpty(login)) throw new InvalidValueException("Login field is empty");
                String password = request.getParameter(Constants.PASSWORD);
                if(StringUtils.isEmpty(password)) throw new InvalidValueException("Password field is empty");
                dataManager.addUser(name, regDate, info, login, password);
                routeToMainPage(request, response);
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.EDIT_USER)) {
                Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                String userXML = XMLHelper.outputXML(dataManager.getUserById(id).toXMLElement());
                session.setAttribute(Constants.USER, userXML);
                getServletContext().getRequestDispatcher("/editUser.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет замену user-а в хранилище данных*/
            else if(action.equals(Constants.DO_EDIT_USER)) {
                Integer id = (Integer) session.getAttribute(Constants.ID);
                String name = request.getParameter(Constants.NAME);
                if(StringUtils.isEmpty(name)) throw new InvalidValueException("Name field is empty");
                Date regDate;
                String setCurrentDateOption = request.getParameter(Constants.DATE_SET_OPTION);
                if(setCurrentDateOption == null) {
                    regDate = parseDate(request);
                } else {
                    regDate = new Date();
                }
                String info = request.getParameter(Constants.INFO);
                String login = request.getParameter(Constants.LOGIN);
                if(StringUtils.isEmpty(login)) throw new InvalidValueException("Login field is empty");
                String password = request.getParameter(Constants.PASSWORD);
                if(StringUtils.isEmpty(password)) throw new InvalidValueException("Password field is empty");
                dataManager.replaceUser(id, name, regDate, info, login, password);
                //на всякий случай...
                session.setAttribute(Constants.ID, null);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.DELETE_USER)) {
                Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                dataManager.deleteUserById(id);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.SEARCH_USER)) {
                String searchParameter = request.getParameter(Constants.USER_SEARCH_PARAMETER);
                Collection<User> users = dataManager.findUsersByName(searchParameter);
                Element usersElement = new Element("Users");
                XMLHelper.addElement(usersElement, Constants.VALUES_XML_TAG, users);
                String usersXML = XMLHelper.outputXML(usersElement);
                session.setAttribute(Constants.FOUND_USERS, usersXML);
                getServletContext().getRequestDispatcher("/userSearchResult.jsp").forward(request, response);
            }

            /*Thread section*/
            /*Асинхронное получение данных о thread-е*/
            else if(action.equals(Constants.GET_THREAD)) {
                String value = (String) request.getParameter(Constants.ID);
                if(!StringUtils.isEmpty(value)) {
                    Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                    String threadXML = XMLHelper.outputXML(dataManager.getThreadById(id).toXMLElement());
                    out.print(threadXML);
                }
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.ADD_THREAD)) {
                session.setAttribute(Constants.THREAD, null);
                getServletContext().getRequestDispatcher("/editThread.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет добавление thread-а в хранилище данных*/
            else if(action.equals(Constants.DO_ADD_THREAD)) {
                String caption = request.getParameter(Constants.CAPTION);
                if(StringUtils.isEmpty(caption)) throw new InvalidValueException("Caption field is empty");
                dataManager.addThread(caption);
                routeToMainPage(request, response);
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.EDIT_THREAD)) {
                Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                String threadXML =  XMLHelper.outputXML(dataManager.getThreadById(id).toXMLElement());
                session.setAttribute(Constants.THREAD, threadXML);
                getServletContext().getRequestDispatcher("/editThread.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет замену thread-а в хранилище данных*/
            else if(action.equals(Constants.DO_EDIT_THREAD)) {
                Integer id = (Integer) session.getAttribute(Constants.ID);
                String caption = request.getParameter(Constants.CAPTION);
                if(StringUtils.isEmpty(caption)) throw new InvalidValueException("Caption field is empty");
                dataManager.replaceThread(id, caption);
                session.setAttribute(Constants.ID, null);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.DELETE_THREAD)) {
                Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                dataManager.deleteThreadById(id);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.SEARCH_THREAD)) {
                String searchParameter = request.getParameter(Constants.THREAD_SEARCH_PARAMETER);
                Collection<ru.ssau.entities.Thread> threads = dataManager.findThreadsByCaption(searchParameter);
                Element threadsElement = new Element("Threads");
                XMLHelper.addElement(threadsElement, Constants.VALUES_XML_TAG, threads);
                String threadsXML = XMLHelper.outputXML(threadsElement);
                session.setAttribute(Constants.FOUND_THREADS, threadsXML);
                getServletContext().getRequestDispatcher("/threadSearchResult.jsp").forward(request, response);
            }

            /*Message section*/
            /*Асинхронное получение данных о message-е*/
            else if(action.equals(Constants.GET_MESSAGE)) {
                String value = (String) request.getParameter(Constants.ID);
                if(!StringUtils.isEmpty(value)) {
                    Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                    String messageXML = XMLHelper.outputXML(dataManager.getMessageById(id).toXMLElement());
                    out.print(messageXML);
                }
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.ADD_MESSAGE)) {
                Element userList = new Element("userList");
                XMLHelper.addElement(userList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(dataManager.getAllUsers()));
                session.setAttribute(Constants.USER_LIST, XMLHelper.outputXML(userList));

                Element threadList = new Element("threadList");
                XMLHelper.addElement(threadList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(dataManager.getAllThreads()));
                session.setAttribute(Constants.THREAD_LIST, XMLHelper.outputXML(threadList));

                session.setAttribute(Constants.MESSAGE, null);
                getServletContext().getRequestDispatcher("/editMessage.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет добавление user-а в хранилище данных*/
            else if(action.equals(Constants.DO_ADD_MESSAGE)) {
                String caption = request.getParameter(Constants.CAPTION);
                if(StringUtils.isEmpty(caption)) throw new InvalidValueException("Caption field is empty");
                String text = request.getParameter(Constants.TEXT);
                if(StringUtils.isEmpty(text)) throw new InvalidValueException("Text field is empty");
                Integer userId = Integer.valueOf(request.getParameter(Constants.USER_ID_IN_MESSAGE));
                Integer threadId = Integer.valueOf(request.getParameter(Constants.THREAD_ID_IN_MESSAGE));
                Date postDate;
                String setCurrentDateOption = request.getParameter(Constants.DATE_SET_OPTION);
                if(setCurrentDateOption == null) {
                    postDate = parseDate(request);
                } else {
                    postDate = new Date();
                }
                dataManager.addMessage(caption, text, userId, threadId, postDate);
                routeToMainPage(request, response);
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.EDIT_MESSAGE)) {
                Element userList = new Element("userList");
                XMLHelper.addElement(userList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(dataManager.getAllUsers()));
                session.setAttribute(Constants.USER_LIST, XMLHelper.outputXML(userList));

                Element threadList = new Element("threadList");
                XMLHelper.addElement(threadList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(dataManager.getAllThreads()));
                session.setAttribute(Constants.THREAD_LIST, XMLHelper.outputXML(threadList));

                Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                Message message = dataManager.getMessageById(id);
                String messageXML =  XMLHelper.outputXML(message.toXMLElement());
                session.setAttribute(Constants.MESSAGE, messageXML);
                getServletContext().getRequestDispatcher("/editMessage.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет замену user-а в хранилище данных*/
            else if(action.equals(Constants.DO_EDIT_MESSAGE)) {
                Integer id = (Integer) session.getAttribute(Constants.ID);
                String caption = request.getParameter(Constants.CAPTION);
                if(StringUtils.isEmpty(caption)) throw new InvalidValueException("Caption field is empty");
                String text = request.getParameter(Constants.TEXT);
                if(StringUtils.isEmpty(text)) throw new InvalidValueException("Text field is empty");
                Integer userId = Integer.valueOf(request.getParameter(Constants.USER_ID_IN_MESSAGE));
                Integer threadId = Integer.valueOf(request.getParameter(Constants.THREAD_ID_IN_MESSAGE));
                Date postDate;
                String setCurrentDateOption = request.getParameter(Constants.DATE_SET_OPTION);
                if(setCurrentDateOption == null) {
                    postDate = parseDate(request);
                } else {
                    postDate = new Date();
                }
                dataManager.replaceMessage(id, caption, text, userId, threadId, postDate);
                session.setAttribute(Constants.ID, null);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.DELETE_MESSAGE)) {
                Integer id = Integer.valueOf(request.getParameter(Constants.ID));
                dataManager.deleteMessageById(id);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.SEARCH_MESSAGE)) {
                String searchParameter = request.getParameter(Constants.MESSAGE_SEARCH_PARAMETER);
                Collection<Message> messages = dataManager.findMessagesByCaption(searchParameter);
                Element messageElement = new Element("Messages");
                XMLHelper.addElement(messageElement, Constants.VALUES_XML_TAG, messages);
                String messagesXML = XMLHelper.outputXML(messageElement);
                session.setAttribute(Constants.FOUND_MESSAGES, messagesXML);
                getServletContext().getRequestDispatcher("/messageSearchResult.jsp").forward(request, response);
            }

        } catch(DataAccessException e) {
            e.printStackTrace();
            System.out.println("DataAccessException @");
            session.setAttribute("message", e.getMessage());
            getServletContext().getRequestDispatcher("/sError.jsp").forward(request, response);
        } catch(InvalidValueException e) {
            e.printStackTrace();
            session.setAttribute("message", e.getMessage());
            getServletContext().getRequestDispatcher("/sError.jsp").forward(request, response);
        }  catch(RemoteException e) {
            e.printStackTrace();
            session.setAttribute("message", e.getMessage());
            getServletContext().getRequestDispatcher("/sError.jsp").forward(request, response);
        }  finally {
            out.close();
        }
    }

    private void routeToMainPage(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String forwardString = String.format("/request?%s=%s", Constants.ACTION, Constants.GET_MODEL_STATE);
        getServletContext().getRequestDispatcher(forwardString).forward(request, response);
    }

    private Date parseDate(HttpServletRequest req) throws InvalidValueException {
        int year = Integer.parseInt(req.getParameter(Constants.YEAR));
        int month = Integer.parseInt(req.getParameter(Constants.MONTH));
        int day = Integer.parseInt(req.getParameter(Constants.DAY));
        int hours = Integer.parseInt(req.getParameter(Constants.HOURS));
        int minutes = Integer.parseInt(req.getParameter(Constants.MINUTES));
        int seconds = Integer.parseInt(req.getParameter(Constants.SECONDS));
        String field = checkDateFields(year, month, day, hours, minutes, seconds);
        if(field != null) {
            throw new InvalidValueException("Invalid date: " + field);
        }
        Calendar c = new GregorianCalendar(year, month-1, day, hours, minutes, seconds);
        return c.getTime();
    }

    /**
     * Проверяет входные параметры на принадлежность допустимым интервалам
     * @param year год.
     * @param month месяц.
     * @param day день.
     * @param hours часы.
     * @param minutes минуты.
     * @param seconds секунды.
     * @return Имя переменной, значение которой не принадлежит допустимому интервалу.
     * Если все значения валидны, возвращает null.
     */
    private String checkDateFields(int year, int month, int day, int hours, int minutes, int seconds) {
        if(year < 1) return "Year";
        if(month < 1 || month > 12) return "Month";
        if(day < 1) return "Day";
        if(hours > 23 || hours < 0) return "Hours";
        if(minutes > 59 || minutes < 0) return "Minutes";
        if(seconds > 59 || seconds < 0) return "Seconds";


        boolean isDayCorrect = true;

        if(month == 1) {
            isDayCorrect &= (day <= 31);
        } else if(month == 2) {
            if(year % 4 == 0) {
                isDayCorrect &= (day <= 29);
            } else {
                isDayCorrect &= (day <= 28);
            }
        } else if(month == 3) {
            isDayCorrect &= (day <= 31);
        } else if(month == 4) {
            isDayCorrect &= (day <= 30);
        } else if(month == 5) {
            isDayCorrect &= (day <= 31);
        } else if(month == 6) {
            isDayCorrect &= (day <= 30);
        } else if(month == 7) {
            isDayCorrect &= (day <= 31);
        } else if(month == 8) {
            isDayCorrect &= (day <= 31);
        } else if(month == 9) {
            isDayCorrect &= (day <= 30);
        } else if(month == 10) {
            isDayCorrect &= (day <= 31);
        } else if(month == 11) {
            isDayCorrect &= (day <= 30);
        } else if(month == 12) {
            isDayCorrect &= (day <= 31);
        }

        if(isDayCorrect) {
            return null;
        } else {
            return "Day";
        }
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /** 
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    } 

    /** 
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "This servlets handles requests to change some forum data";
    }// </editor-fold>

}
